package com.bii.gelapp;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

import org.opencv.android.Utils;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Scalar;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.View.OnTouchListener;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;

public class MarkerTouchImageView extends ImageView {
	Matrix matrix;

	// We can be in one of these 3 states
	static final int NONE = 0;
	static final int DRAG = 1;
	static final int ZOOM = 2;
	static final int CLICK = 3;
	int mode = NONE;

	boolean editmode = false;
	boolean erasemode = false;
	boolean valuemode = false;

	PointF last = new PointF();
	PointF start = new PointF();
	float minScale = 1f;
	float maxScale = 8f;
	float[] m;

	int viewWidth, viewHeight;
	float saveScale = 1f;
	protected float origWidth, origHeight;
	int oldMeasuredWidth, oldMeasuredHeight;

	public Bitmap originalBmp;
	public Bitmap activeBmp;

	ScaleGestureDetector mScaleDetector;

	GestureDetector gestureDetector;

	Context context;

	Bitmap bmap;
	Mat drawmat;
	ArrayList<ConnectedComponent> markerlist = new ArrayList<ConnectedComponent>();

	// arraylist to store the circles
	ArrayList<ConnectedComponent> addedmarkerlist = new ArrayList<ConnectedComponent>();
	// to store colors
	ArrayList<int[]> colorlist = new ArrayList<int[]>();

	// for connected component cc
	float rtop = 0;
	float rleft = 0;
	float rright = 0;
	float rbottom = 0;

	// for connected component dc
	float dtop = 0;
	float dleft = 0;
	float dright = 0;
	float dbottom = 0;

	// for image bounds
	float imagetop = 0;
	float imagebottom = 0;
	float imageleft = 0;
	float imageright = 0;

	float imagewidth;
	float imageheight;

	

	// set maximum zoom for matrix
	public void setMaxZoom(float x) {
		maxScale = x;
	}

	// used especially for setting the mode to edit
	public void setEditMode(boolean edit) {
		editmode = edit;
	}

	public void setEraseMode(boolean erase) {
		erasemode = erase;
	}

	public void setValuemode(boolean markers) {
		valuemode = markers;
	}

	public MarkerTouchImageView(Context context, Bitmap bmp) {
		super(context);
		originalBmp = bmp;
		sharedConstructing(context);
	}

	private void sharedConstructing(Context context) {
		// preparation for all the image loading and stuffs
		super.setClickable(true);
		this.context = context;

		/*
		 * if (circlelist.size() > 0) { drawCircles(); }
		 */

		bmap = originalBmp;
		drawmat = new Mat(bmap.getHeight(), bmap.getWidth(), CvType.CV_8U);
		// get all component list from fullactivity
		markerlist = MarkerFullscreenActivity.getMarkerlist();
		if (markerlist.size() != 0) {
			for (int i = 0; i < markerlist.size(); i++) {
				addedmarkerlist.add(markerlist.get(i));

			}
		}

		if (addedmarkerlist.size() > 0) {
			// drawMarkers();
			drawRectangles();
		}

		

		mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
		matrix = new Matrix();
		m = new float[9];
		setImageMatrix(matrix);
		// allow scaling of image by matrix
		setScaleType(ScaleType.MATRIX);

		setOnTouchListener(new OnTouchListener() {

			@Override
			public boolean onTouch(View v, MotionEvent event) {
				mScaleDetector.onTouchEvent(event);
				PointF curr = new PointF(event.getX(), event.getY());
				long starttime = 0;
				long endtime = 0;

				ConnectedComponent cc = new ConnectedComponent();
				// use to show the current rectangle drawn
				ConnectedComponent dc = new ConnectedComponent();

				switch (event.getAction()) {
				case MotionEvent.ACTION_DOWN:
					last.set(curr);
					start.set(last);
					mode = DRAG;
					Log.d("tag1", "addedmarkerlist: " + addedmarkerlist.size());
					if (editmode == true) {
						Matrix inverse = new Matrix();
						getImageMatrix().invert(inverse);
						float[] touchPoint = new float[] { event.getX(),
								event.getY() };
						inverse.mapPoints(touchPoint);
						rtop = touchPoint[1];
						rleft = touchPoint[0];
						

						dtop = touchPoint[1];
						dleft = touchPoint[0];

					}

					if (erasemode == true) {
						bmap = originalBmp;
						drawmat = new Mat(bmap.getHeight(), bmap.getWidth(),
								CvType.CV_8U);
						// get the coordinates according to image
						Matrix inverse = new Matrix();
						getImageMatrix().invert(inverse);
						float[] touchPoint = new float[] { event.getX(),
								event.getY() };

						inverse.mapPoints(touchPoint);
						for (int i = 0; i < addedmarkerlist.size(); i++) {
							float itop = addedmarkerlist.get(i).top;
							float ileft = addedmarkerlist.get(i).left;
							float iright = addedmarkerlist.get(i).right;
							float ibottom = addedmarkerlist.get(i).bottom;

							if (itop > ibottom) {
								float temp;
								temp = itop;
								itop = ibottom;
								ibottom = temp;
							}
							if (ileft > iright) {
								float temp;
								temp = ileft;
								ileft = iright;
								iright = temp;
							}

							if (touchPoint[0] <= iright
									&& touchPoint[0] >= ileft) {
								if (touchPoint[1] <= ibottom
										&& touchPoint[1] >= itop) {
									addedmarkerlist.remove(i);
									drawRectangles();
								}
							}

						}

					}

					break;

				case MotionEvent.ACTION_MOVE:
					if (editmode == true) {
						mode = NONE;
						Matrix inverse = new Matrix();
						getImageMatrix().invert(inverse);
						float[] touchPoint = new float[] { event.getX(),
								event.getY() };
						inverse.mapPoints(touchPoint);
						rbottom = touchPoint[1];
						rright = touchPoint[0];

						// dc
						dc = new ConnectedComponent();
						dbottom = touchPoint[1];
						dright = touchPoint[0];

						dc.left = (int) dleft;
						dc.right = (int) dright;
						dc.top = (int) dtop;
						dc.bottom = (int) dbottom;

						drawcurrentrectangle();

					}

					if (mode == DRAG) {

						// get the new horizontal and vertical positions
						float deltaX = curr.x - last.x;
						float deltaY = curr.y - last.y;
						// if view width is larger than content size
						// (original
						// width and height) returns 0
						float fixTransX = getFixDragTrans(deltaX, viewWidth,
								origWidth * saveScale);
						float fixTransY = getFixDragTrans(deltaY, viewHeight,
								origHeight * saveScale);
						matrix.postTranslate(fixTransX, fixTransY);
						fixTrans();
						last.set(curr.x, curr.y);
						
					}

					break;

				case MotionEvent.ACTION_UP:

					mode = NONE;

					int xDiff = (int) Math.abs(curr.x - start.x);
					int yDiff = (int) Math.abs(curr.y - start.y);
					if (xDiff < CLICK && yDiff < CLICK)
						performClick();

					float imageviewheight = getMeasuredHeight();// height of
					// imageView
					float imageviewwidth = getMeasuredWidth();
					Drawable drawable = getDrawable();
					imagewidth = drawable.getIntrinsicWidth();
					imageheight = drawable.getIntrinsicHeight();

					float width = imagewidth;
					float height = imageheight;
					/*
					if (imageviewheight / imageheight <= imageviewwidth
							/ imagewidth)
						width = imagewidth * imageviewheight / imageheight;// rescaled
																			// width
																			// of
																			// image
																			// within
																			// ImageView
					else
						height = imageviewheight * imageviewwidth / imagewidth;
					*/
					

					if (editmode == true) {
						Log.d("tag1", "width: " + width);
						Log.d("tag1", "height: " + height);
						
						Log.d("tag1", "rtop: " + rtop);
						Log.d("tag1", "rleft: " + rleft);
						Log.d("tag1", "rright: " + rright);
						Log.d("tag1", "rbottom: " + rbottom);

						//if (rtop > getTop() && rbottom < getBottom()
						//		&& rleft > getLeft() && rright < getRight()) {
						
						float ivRight = getLeft() + width;
						float ivBottom = getTop() + height;
						
						Log.d("tag1", "ivtop: " + getTop());
						Log.d("tag1", "ivleft: " + getLeft());
						Log.d("tag1", "ivright: " + ivRight);
						Log.d("tag1", "ivbottom: " + ivBottom);
						
						if (rtop > rbottom) {
							float temp;
							temp = rtop;
							rtop = rbottom;
							rbottom = temp;
						}
						if (rleft > rright) {
							float temp;
							temp = rleft;
							rleft = rright;
							rright = temp;
						}
						
						if(rtop > getTop() && rbottom < ivBottom && rleft > getLeft() && rright < ivRight){
							cc.left = (int) rleft;
							cc.right = (int) rright;
							cc.top = (int) rtop;
							cc.bottom = (int) rbottom;
							
							if (rtop > rbottom) {
								int temp;
								temp = cc.top;
								cc.top = cc.bottom;
								cc.bottom = temp;
							}
							if (rleft > rright) {
								int temp;
								temp = cc.left;
								cc.left = cc.right;
								cc.right = temp;
							}

							addedmarkerlist.add(cc);
							drawRectangles();
						} else {
							drawRectangles();
							Log.d("tag1", "no drawn rectangle");
							break;
						}

					}

					Log.d("tag1", "addedmarkerlist: " + addedmarkerlist.size());

					break;

				case MotionEvent.ACTION_POINTER_UP:

					mode = NONE;

					break;
				}

				setImageMatrix(matrix);

				invalidate();

				return true; // indicate event was handled
			}

		});
	}

	private void drawcurrentrectangle() {
		Utils.bitmapToMat(bmap, drawmat);
		Core.rectangle(drawmat, new Point(dleft, dtop), new Point(dright,
				dbottom), new Scalar(255, 0, 0), 2);

		Bitmap drawnbmp = Bitmap.createBitmap(drawmat.cols(), drawmat.rows(),
				Bitmap.Config.RGB_565);
		Utils.matToBitmap(drawmat, drawnbmp);
		setImageBitmap(drawnbmp);
	}

	private void drawRectangles() {
		Utils.bitmapToMat(bmap, drawmat);
		for (int i = 0; i < addedmarkerlist.size(); i++) {
			Core.rectangle(
					drawmat,
					new Point(addedmarkerlist.get(i).left, addedmarkerlist
							.get(i).top),
					new Point(addedmarkerlist.get(i).right, addedmarkerlist
							.get(i).bottom), new Scalar(255, 255, 0), 2);
		}
		Bitmap drawnbmp = Bitmap.createBitmap(drawmat.cols(), drawmat.rows(),
				Bitmap.Config.RGB_565);
		Utils.matToBitmap(drawmat, drawnbmp);
		setImageBitmap(drawnbmp);

	};

	private void getMarkerColors() {
		bmap = originalBmp;
		drawmat = new Mat(bmap.getHeight(), bmap.getWidth(), CvType.CV_8U);
		for (int i = 0; i < addedmarkerlist.size(); i++) {
			// get center horizontal
			int horizontalmid = (addedmarkerlist.get(i).left + addedmarkerlist
					.get(i).right) / 2;
			// get center vertical
			int verticalmid = (addedmarkerlist.get(i).top + addedmarkerlist
					.get(i).bottom) / 2;
			int pixel = bmap.getPixel(horizontalmid, verticalmid);
			int redValue = Color.red(pixel);
			int blueValue = Color.blue(pixel);
			int greenValue = Color.green(pixel);
			// Scalar color = new Scalar(redValue, blueValue, greenValue);
			// R, G, B
			int[] colorvalues = { redValue, greenValue, blueValue };
			colorlist.add(colorvalues);
		}
	}

	private class ScaleListener extends
			ScaleGestureDetector.SimpleOnScaleGestureListener {

		@Override
		public boolean onScaleBegin(ScaleGestureDetector detector) {
			mode = ZOOM;
			return true;
		}

		@Override
		public boolean onScale(ScaleGestureDetector detector) {
			if (editmode != true) {
				float mScaleFactor = detector.getScaleFactor();
				float origScale = saveScale;
				saveScale *= mScaleFactor;

				if (saveScale > maxScale) {
					// prevent zooming in too much
					saveScale = maxScale;
					mScaleFactor = maxScale / origScale;
				} else if (saveScale < minScale) {
					// prevent zooming out too much
					saveScale = minScale;
					mScaleFactor = minScale / origScale;
				}

				if (origWidth * saveScale <= viewWidth
						|| origHeight * saveScale <= viewHeight)
					matrix.postScale(mScaleFactor, mScaleFactor, viewWidth / 2,
							viewHeight / 2);
				else
					matrix.postScale(mScaleFactor, mScaleFactor,
							detector.getFocusX(), detector.getFocusY());

				fixTrans();

				Log.d("tag1", "Bitmap size: "
						+ ((BitmapDrawable) getDrawable()).getBitmap()
								.getWidth()
						+ " "
						+ ((BitmapDrawable) getDrawable()).getBitmap()
								.getHeight());
				Log.d("tag1", "Image view size: " + getWidth() + " "
						+ getHeight());

			}
			return true;
		}

	}

	void fixTrans() {
		matrix.getValues(m);
		float transX = m[Matrix.MTRANS_X];
		float transY = m[Matrix.MTRANS_Y];

		float fixTransX = getFixTrans(transX, viewWidth, origWidth * saveScale);
		float fixTransY = getFixTrans(transY, viewHeight, origHeight
				* saveScale);
		// if view height and width is not larger than original height and
		// weight, matrix would be created
		if (fixTransX != 0 || fixTransY != 0)
			matrix.postTranslate(fixTransX, fixTransY);
	}

	float getFixTrans(float trans, float viewSize, float contentSize) {
		float minTrans, maxTrans;

		if (contentSize <= viewSize) {
			minTrans = 0;
			maxTrans = viewSize - contentSize;
		} else {
			minTrans = viewSize - contentSize;
			maxTrans = 0;
		}

		if (trans < minTrans)
			return -trans + minTrans;
		if (trans > maxTrans)
			return -trans + maxTrans;
		return 0;
	}

	float getFixDragTrans(float delta, float viewSize, float contentSize) {
		if (contentSize <= viewSize) {
			return 0;
		}
		return delta;
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		viewWidth = MeasureSpec.getSize(widthMeasureSpec);
		viewHeight = MeasureSpec.getSize(heightMeasureSpec);

		//
		// Rescales image on rotation
		//
		if (oldMeasuredHeight == viewWidth && oldMeasuredHeight == viewHeight
				|| viewWidth == 0 || viewHeight == 0)
			return;
		oldMeasuredHeight = viewHeight;
		oldMeasuredWidth = viewWidth;

		if (saveScale == 1) {
			// Fit to screen.
			float scale;

			Drawable drawable = getDrawable();
			if (drawable == null || drawable.getIntrinsicWidth() == 0
					|| drawable.getIntrinsicHeight() == 0)
				return;
			int bmWidth = drawable.getIntrinsicWidth();
			int bmHeight = drawable.getIntrinsicHeight();

			Log.d("bmSize", "bmWidth: " + bmWidth + " bmHeight : " + bmHeight);

			float scaleX = (float) viewWidth / (float) bmWidth;
			float scaleY = (float) viewHeight / (float) bmHeight;
			scale = Math.min(scaleX, scaleY);
			matrix.setScale(scale, scale);

			// Center the image
			float redundantYSpace = (float) viewHeight
					- (scale * (float) bmHeight);
			float redundantXSpace = (float) viewWidth
					- (scale * (float) bmWidth);
			redundantYSpace /= (float) 2;
			redundantXSpace /= (float) 2;

			matrix.postTranslate(redundantXSpace, redundantYSpace);

			origWidth = viewWidth - 2 * redundantXSpace;
			origHeight = viewHeight - 2 * redundantYSpace;
			setImageMatrix(matrix);
		}
		fixTrans();
	}

	public void confirmation() {
		// sort arraylist based on how high it is
		Collections.sort(addedmarkerlist, new Comparator<ConnectedComponent>() {
			@Override
			public int compare(ConnectedComponent cc1, ConnectedComponent cc2) {
				return cc1.top - cc2.top;
			}
		});

		markerlist = addedmarkerlist;
		MarkerFullscreenActivity.getMarkerlist().clear();
		for (int i = 0; i < addedmarkerlist.size(); i++) {
			MarkerFullscreenActivity.getMarkerlist()
					.add(addedmarkerlist.get(i));
		}
		getMarkerColors();
		for (int i = 0; i < colorlist.size(); i++) {
			MarkerFullscreenActivity.getColorlist().add(colorlist.get(i));
		}
		Bitmap drawnbmp = Bitmap.createBitmap(drawmat.cols(), drawmat.rows(),
				Bitmap.Config.RGB_565);
		Utils.matToBitmap(drawmat, drawnbmp);
		MarkerFullscreenActivity.activeBmp = drawnbmp.copy(
				Bitmap.Config.ARGB_8888, true);

	}

	@Override
	public void onWindowFocusChanged(boolean hasFocus) {
		super.onWindowFocusChanged(hasFocus);
		// Button button = (Button) findViewById(R.id.button2);
		// System.out.println(button.getLeft());
		// System.out.println(button.getRight());
		// Log.d("tag1", "Left: " + getLeft());
		// Log.d("tag1", "Right: " + getRight());
		// Log.d("tag1", "Top: " + getTop());
		// Log.d("tag1", "Bottom: " + getBottom());
	}

}
